package com.checkbox.widgetset;

import com.checkbox.widgetset.client.conn.SelectableTreeNodeServerRpc;
import com.checkbox.widgetset.client.share.SelectableTreeNodeState;
import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.converter.Converter.ConversionException;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.Resource;
import com.vaadin.server.ResourceReference;
import com.vaadin.ui.AbstractComponentContainer;
import com.vaadin.ui.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@SuppressWarnings("serial")
public class SelectableTreeNode extends AbstractComponentContainer {
  private class PropChangeEvent extends Component.Event implements Property.ValueChangeEvent {
    public PropChangeEvent(Component component) {
      super(component);
    }

    @Override
    public Property getProperty() {
      return new Property() {
        @Override
        public Class<?> getType() {
          return Boolean.class;
        }

        @Override
        public Object getValue() {
          return SelectableTreeNode.this.isSelect();
        }

        @Override
        public boolean isReadOnly() {
          return true;
        }

        @Override
        public void setReadOnly(boolean newStatus) {
        }

        @Override
        public void setValue(Object newValue) throws ReadOnlyException, ConversionException {
          throw new ReadOnlyException();
        }
      };
    }
  }

  private final List<Component> components = new ArrayList<Component>();
  private final Span displayLabel;
  private Object itemId;
  private final List<SelectableTreeNode> nodes = new ArrayList<SelectableTreeNode>();
  private final SelectableTreeNodeServerRpc serverRpc = new SelectableTreeNodeServerRpc() {
    @Override
    public void onSelected(boolean selected) {
      getState().isSelect = selected;
      PropChangeEvent event = new PropChangeEvent(SelectableTreeNode.this);
      SelectableTreeNode.this.fireEvent(event);
    }
  };

  public SelectableTreeNode(Object itemId) {
    this.itemId = itemId;
    displayLabel = new Span();
    this.addComponent(displayLabel);
    registerRpc(serverRpc);
  }

  public SelectableTreeNode(Object itemId, String displayStr, Object data) {
    this(itemId);
    setData(data);
    if (displayStr != null) {
      setDisplayStr(displayStr);
    }
  }

  public SelectableTreeNode addChild(Object itemId, Object data) {
    return addChild(itemId, null, data);
  }

  public SelectableTreeNode addChild(Object itemId, String displayStr, Object data) {
    SelectableTreeNode newChild = new SelectableTreeNode(itemId, displayStr, data);
    this.addComponent(newChild);
    return newChild;
  }

  @Override
  public void addComponent(Component c) {
    if (c instanceof SelectableTreeNode) {
      nodes.add((SelectableTreeNode) c);
    }
    components.add(c);
    super.addComponent(c);
  }

  @Deprecated
  public void addListener(ValueChangeListener listener) {
    addValueChangeListener(listener);
  }

  public void addValueChangeListener(ValueChangeListener listener) {
    Method m;
    try {
      m =
          Property.ValueChangeListener.class.getDeclaredMethod("valueChange",
              new Class[] {Property.ValueChangeEvent.class});
      super.addListener(Property.ValueChangeEvent.class, listener, m);
    } catch (SecurityException e) {
    } catch (NoSuchMethodException e) {
    }
  }

  public void clearChild() {
    super.removeAllComponents();
    components.clear();
    nodes.clear();
  }

  public void clearSelect() {
    setSelect(false);
    for (SelectableTreeNode node : this.nodes) {
      node.clearSelect();
    }
  }

  public String getChildStyleName() {
    return getState().childStyleName;
  }

  public String getCollsIconPath() {
    return getState().collsIcon.getURL();
  }

  @Override
  public int getComponentCount() {
    return components.size();
  }

  @Deprecated
  public String getDisplayStr() {
    return "";
  }

  public String getExpandIconPath() {
    return getState().expandIcon.getURL();
  }

  public Object getItemId() {
    return itemId;
  }

  public String getNodeStyleName() {
    return getState().nodeStyleName;
  }

  public List<Object> getSelectedChildren() {
    List<Object> selectedChild = new ArrayList<Object>();
    for (SelectableTreeNode node : nodes) {
      if (node.isSelect()) {
        selectedChild.add(node.getItemId());
      }
      selectedChild.addAll(node.getSelectedChildren());
    }
    return selectedChild;
  }

  public boolean isCheckable() {
    return getState().isCheckable;
  }

  public boolean isCollsable() {
    return getState().isCollsable;
  }

  public boolean isExpand() {
    return getState().isExpand;
  }

  public boolean isExpandIconAlignRight() {
    return getState().isExpandIconAlignRight;
  }

  public boolean isSelect() {
    return getState().isSelect;
  }

  @Override
  public Iterator<Component> iterator() {
    return components.iterator();
  }

  public void removeChild(SelectableTreeNode child) {
    removeComponent(child);
  }

  @Override
  public void removeComponent(Component c) {
    nodes.remove(c);
    components.remove(c);
    super.removeComponent(c);
  }

  @Deprecated
  public void removeListener(ValueChangeListener listener) {
    removeValueChangeListener(listener);
  }

  public void removeValueChangeListener(ValueChangeListener listener) {
    super.removeListener(Property.ValueChangeEvent.class, listener);
  }

  @Override
  public void replaceComponent(Component oldComponent, Component newComponent) {
    // TODO Auto-generated method stub
  }

  public void setCheckable(boolean isCheckable) {
    getState().isCheckable = isCheckable;
  }

  public void setChildStyleName(String childStyleName) {
    getState().childStyleName = childStyleName;
  }

  public void setCollsable(boolean isCollsable) {
    getState().isCollsable = isCollsable;
  }

  public void setCollsIcon(Resource collsIcon) {
    getState().collsIcon = ResourceReference.create(collsIcon, this, null);
  }

  @Deprecated
  public void setCollsIconPath(String collsIconPath) {
    setCollsIcon(new ExternalResource(collsIconPath));
  }

  public void setDisplayStr(String displayStr) {
    displayLabel.setText(displayStr);
  }

  public void setExpand(boolean isExpand) {
    getState().isExpand = isExpand;
  }

  public void setExpandIcon(Resource expandIcon) {
    getState().expandIcon = ResourceReference.create(expandIcon, this, null);
  }

  public void setExpandIconAlignRight(boolean isExpandIconAlignRight) {
    getState().isExpandIconAlignRight = isExpandIconAlignRight;
  }

  @Deprecated
  public void setExpandIconPath(String expandIconPath) {
    setExpandIcon(new ExternalResource(expandIconPath));
  }

  public void setItemId(Object itemId) {
    this.itemId = itemId;
  }

  public void setNodeStyleName(String nodeStyleName) {
    getState().nodeStyleName = nodeStyleName;
  }

  public void setSelect(boolean isSelect) {
    getState().isSelect = isSelect;
  }

  @Override
  protected SelectableTreeNodeState getState() {
    return (SelectableTreeNodeState) super.getState();
  }
}
